/* This file is part of SpaceSim.
 
 SpaceSim is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.
 
 SpaceSim is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with SpaceSim.  If not, see <http://www.gnu.org/licenses/>. */

#include "graphics.h"
#include "language.h"
#include "main.h"
#include "random.h"
#include "system.h"

System::System() {
	Mass = 2e30;
	PlanetAmount = 0;
}

System::System(int seed) {
	this->SetRandom(seed);
}


System::System(float _mass) {
	Mass = _mass;
	PlanetAmount = 0;
}

void System::MakeHub() {	// make a cluster hub
	Planets.clear();

	Name = "Cluster Hub";
		
	Planets.push_back(Planet(400.0, 10000, 2e30, NULL, PlanetType::WarpLauncher, 300.0, 1.0));	// add a warp launcher
	Planets.push_back(Planet(450.0, 40000, 2e30, NULL, PlanetType::HubLauncher, 300.0, 1.0));	// add a warp hub
	Planets.push_back(Planet(500.0, 10000, 2e30, NULL, PlanetType::FreeStation, 300.0, 1.0));	// add a defence and/or control station

	PlanetAmount = 3;
}

void System::SetRandom(int seed) {	// make a random system
	Planets.clear();
	SystemSeed = seed;
	Random::Seed(seed);
	
	// Generate a name for the star
	Name = Language::RandomSystemName();
	
	Random::Seed(seed);
	
	float starMass = Mass = Random::NormalRand(2e29, 2e31);
	
	Planets.push_back(Planet(400.0, 10000, starMass, NULL, PlanetType::WarpLauncher, 300.0, 1.0));	// add a warp launcher
		
	// the border between the terrestrial and the jovian planets
	// should be at 3 - 4 AU
	// 4500 - 6000 spaceunits
	float terrestrialJovianBorder = Random::Rand(4500.f, 6000.f);
	
	// the border between jovian planets and the Kuiper belt
	// should be at 30 - 40 AU
	// 45000 - 60000 spaceunits
	float jovianKuiperBorder = Random::Rand(45000.f, 60000.f);
	
	// generate the terrestrial planets
	// first, an orbit that is between 500.0 su (too hot) and the TJB (gaseous)
	// next, generate a temperature based off the orbit
	// if the temperature is at or under liquid water, give it a chance for water
	// then, make an atmosphere
	// water + atmosphere determine final temperature
	
	int terrestrialPlanets = Random::NormalRand(0.0, 10.0);
	
	for (int i = 0; i < terrestrialPlanets; i++) {
		float orbit = Random::Rand(500.0, terrestrialJovianBorder);
		float atmosphere = Random::ExponentialRand(1.0);
		float mass = Random::NormalRand((float)1.0e23, (float)1.0e25);
		atmosphere -= 1.0;	// a part of the atmosphere is blown away by solar wind
		if (atmosphere > 2.0) { // dense atmosphere - high greenhouse effect - high temp
			Planets.push_back(Planet(orbit, mass, starMass, NULL, PlanetType::Venusian, MakeTemperature(PlanetType::Venusian, orbit, atmosphere), atmosphere));
		} else if (atmosphere < 0.0) {
			// no atmosphere
			bool isQuartz = Random::Rand(0, 1);
			if (isQuartz) {
				Planets.push_back(Planet(orbit, mass, starMass, NULL, PlanetType::Quartz, MakeTemperature(PlanetType::Quartz, orbit, atmosphere), atmosphere));
			} else {
				Planets.push_back(Planet(orbit, mass, starMass, NULL, PlanetType::Rocky, MakeTemperature(PlanetType::Rocky, orbit, atmosphere), atmosphere));
			}
		} else if (atmosphere < 0.5) {	// altitude sickness starts at .75, but above ~ .50 real symptoms appear
			// thin atmosphere
			bool isQuartz = Random::Rand(0, 1);
			if (isQuartz) {
				Planets.push_back(Planet(orbit, mass, starMass, NULL, PlanetType::Quartz, MakeTemperature(PlanetType::Quartz, orbit, atmosphere), atmosphere));
			} else {
				Planets.push_back(Planet(orbit, mass, starMass, NULL, PlanetType::Martian, MakeTemperature(PlanetType::Martian, orbit, atmosphere), atmosphere));
			}
		} else {	// good atmospheric density for humans
			float temperature = MakeTemperature(PlanetType::Atmos, orbit, atmosphere);	// approximate the temperature for the current logic
			if (temperature > 330.0) { // water boils away
				Planets.push_back(Planet(orbit, mass, starMass, NULL, PlanetType::Atmos, MakeTemperature(PlanetType::Atmos, orbit, atmosphere), atmosphere));
			} else {
				float waterCoverage = Random::Rand(0.f, 1.f);
				if (waterCoverage > 0.4 && temperature < 0.0) {
					Planets.push_back(Planet(orbit, mass, starMass, NULL, PlanetType::Ice, MakeTemperature(PlanetType::Ice, orbit, atmosphere), atmosphere));
				} else if (temperature < 230.0) {
					Planets.push_back(Planet(orbit, mass, starMass, NULL, PlanetType::Atmos, MakeTemperature(PlanetType::Atmos, orbit, atmosphere), atmosphere));	
				} else if (waterCoverage > 0.4 && waterCoverage < 0.6 && temperature < 280.0) {
					Planets.push_back(Planet(orbit, mass, starMass, NULL, PlanetType::Habitable, MakeTemperature(PlanetType::Habitable, orbit, atmosphere), atmosphere));
				} else if (waterCoverage > 0.4) {
					Planets.push_back(Planet(orbit, mass, starMass, NULL, PlanetType::Water, MakeTemperature(PlanetType::Water, orbit, atmosphere), atmosphere));
				} else {
					Planets.push_back(Planet(orbit, mass, starMass, NULL, PlanetType::Atmos, MakeTemperature(PlanetType::Atmos, orbit, atmosphere), atmosphere));
				}
			}
		}
		// add a moon!
		int moonAmount = (int)Random::NormalRand(0.f, 5.f);
		Planet *basePlanet = &(*(Planets.end()-1));	// basePlanet points to the last element - the planet we just inserted
		for (int j = 0; j < moonAmount; j++) {
			Planet Moon(Random::NormalRand(0.1f, 100.f), Random::NormalRand((float)1.0e16, (float)10e22), Mass, basePlanet, PlanetType::Rocky, 200.0, 0.0);
			basePlanet->AddMoon(Moon); // add a moon to the planet
		}
	}
	
	// generate jovian planets
	// these are really quite big
	
	int jovianPlanets = Random::NormalRand(0.f, 10.f);
	
	for (int i = 0; i < jovianPlanets; i++) {
		float orbit = Random::Rand(terrestrialJovianBorder, jovianKuiperBorder);
		float mass = Random::NormalRand((float)1.0e25, (float)1.0e28);
		float temperature = MakeTemperature(PlanetType::GasGiant, orbit, 100);
		Planets.push_back(Planet(orbit, mass, starMass, NULL, PlanetType::GasGiant, temperature, 100.0));
		
		int moonAmount = (int)Random::NormalRand(0.f, 5.f);
		Planet *basePlanet = &(*(Planets.end()-1));	// basePlanet points to the last element - the planet we just inserted
		for (int j = 0; j < moonAmount; j++) {
			Planet Moon(Random::NormalRand(0.1f, 100.f), Random::NormalRand((float)1.0e16, (float)10e22), Mass, basePlanet, PlanetType::Rocky, temperature, 0.0);
			basePlanet->AddMoon(Moon); // add a moon to the planet
		}
	}
	
	// generate kuiper belt objects
	// these are small and there are a lot
	// so we just draw the biggest ones
	
	int kuiperPlanets = Random::NormalRand(0.f, 10.f);
	
	for (int i = 0; i < kuiperPlanets; i++) {
		float orbit = Random::Rand(jovianKuiperBorder, 300000.f);
		float mass = Random::NormalRand((float)1.0e21, (float)1.0e23);
		if (Random::Rand(0, 1))
			Planets.push_back(Planet(orbit, mass, starMass, NULL, PlanetType::Rocky, MakeTemperature(PlanetType::Rocky, orbit, 0.0), 0.0));
		else 
			Planets.push_back(Planet(orbit, mass, starMass, NULL, PlanetType::Ice, MakeTemperature(PlanetType::Ice, orbit, 0.0), 0.0));
	}
	sort (Planets.begin(), Planets.end(), IsOrbitRadiusSmaller);
	
	PlanetAmount = 1 + terrestrialPlanets + jovianPlanets + kuiperPlanets;	// the 1 is the warp launcher
}

void System::SetMass(float _mass) {	// getters and setters
	Mass = _mass;
}

float System::GetMass() {
	return Mass;
}

void System::AddPlanet(Planet _planet) {
	Planets.push_back(_planet);
	PlanetAmount++;
}

void System::SetPlanet(Planet _planet, int _number) {
	if (_number > PlanetAmount) {	// the number is too big
		return;
	}
	Planets[_number] = _planet;
}

Planet System::GetPlanet(int _number) {
	if (_number > PlanetAmount) {	// the number is too big
		return Planet();
	}
	return Planets[_number];
}

Planet *System::GetPlanetPtr(int _number) {
	if (_number > PlanetAmount) {	// the number is too big
		return NULL;
	}
	return &Planets[_number];
}

int System::GetPlanetAmount() {
	return PlanetAmount;
}

std::vector<Planet> System::GetPlanets() {
	return Planets;
}

std::string System::GetName() {
	return Name;
}

void System::Update() {
	for (std::vector<Planet>::iterator planetIterator = Planets.begin(); planetIterator < Planets.end(); planetIterator++) {	// update the planets in the system
		planetIterator->Update();
	}
}

void System::Draw() {
	std::vector<Planet>::iterator planetIterator;
	
	int i = 0;
	
	for (planetIterator=Planets.begin(); planetIterator < Planets.end(); planetIterator++) {
		float radius = 3.0;
		planetIterator->Draw();
		i++;
	}
	Graphics::App.Draw(sf::Shape::Circle(0.0, 0.0, 10.0, sf::Color::Yellow));	// draw the central sun
}

void System::DrawInfo() {
	std::vector<Planet>::iterator planetIterator;
	
	int i = 1;
	
	for (planetIterator=Planets.begin(); planetIterator < Planets.end(); planetIterator++) {
		float radius = 3.0;
		planetIterator->DrawInfo(i);
		i++;
	}
	
	sf::String MainBodyInfoString("0 - Star, \"" + Name + "\"");
	MainBodyInfoString.SetSize(15);
	MainBodyInfoString.SetPosition(20, 20);
	
	Graphics::App.Draw(MainBodyInfoString);	// draw the central sun
}

void System::DrawWarp(int num) {
	sf::String InfoString(Name);
	InfoString.SetSize(15);
	InfoString.SetPosition(20, 20 + 30 * num);
	Graphics::App.Draw(InfoString);	// draw info
}